/*
 * AbstractSimpleHierarchy.java
 *
 * Created on October 26, 2007, 9:10 AM
 *
 */
package HierarchyPackage.SimpleHierarchy;

import HierarchyPackage.*;
import HierarchyPackage.AffixHierarchies.HClassTuple;
import ClassifiedDataSetPackage.ClassifiedDataSet;
import ClassifiedDataSetPackage.ClassifiedDataSetStorage;
import DataSetPackage.DataSetException;
import DataSetPackage.SimpleView;
import DataSetPackage.View;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.osdtsystem.utils.MemoryStorage;

/**
 *
 * @author Martin Haulrich
 */
abstract class AbstractSimpleHierarchy implements SimpleHierarchy {

    /**
     * Newline character.
     */
    static String newline = System.getProperty("line.separator");

    /**
     * The type of hierarchy represented by this class
     */
    final String hierarchyType = "SimpleHierarchy";

    /**
     * Counter for how many classes has been added
     * Used two find unique IDs for new classes
     */
    int classCounter;

    /**
     * Flag to tell whether or not children of a node representes discunt sets.
     */
    boolean disjunctive;

    /**
     * The TOP-class for this hierarchy.
     */
    SimpleHClass top;

    Map<HClassTuple, Boolean> superClassOfCache;

    public AbstractSimpleHierarchy() {
        superClassOfCache = new LinkedHashMap<HClassTuple, Boolean>(1280, 0.75F, true) {

            @Override
            protected boolean removeEldestEntry(Map.Entry<HClassTuple, Boolean> eldest) {
                return size() > 1000;
            }
        };
    }

    /**
     * Returns the name of the class with the given ID
     * @return Name of the class
     * @param classID ID of class
     * Thrown if their are problems with accesing the hierarchy.
     */
    abstract String getName(int classID);

    @Override
    public String getName(HClass c) {
        SimpleHClass sc = (SimpleHClass) c;
        return getName(sc.getID());
    }

    /**
     * Get superclasses for a given class.
     * @param classID Class to get superclasses for.
     * Thrown if their are problems with accesing the hierarchy.
     * @return Array containing the superclasses.
     */
    abstract int[] getSuperclasses(int classID);

    @Override
    public HClass getTop() {
        return top;
    }

    public synchronized SimpleHClass getHClass(int c) {
        return new SimpleHClass(this, c);
    }

    @Override
    public ClassifiedDataSet getClassifications(Collection<Integer> dids, View v, HClass topClass,
            int maxDepth, int minCount, int cutFraction, boolean saveData) {

        /**
         * @TODO minCount currently not in use
         */
        /**
         * @TODO saveData currently not in use
         */
        ClassifiedDataSetStorage result = new ClassifiedDataSetStorage(new MemoryStorage(), minCount, cutFraction);

        if ((topClass instanceof SimpleHClass) && (v instanceof SimpleView)) {

            SimpleHClass tC = (SimpleHClass) topClass;
            SimpleView dv = (SimpleView) v;


            Runnable r = new SimpleThread(dids, dv, tC, maxDepth, result);
            Thread t = new Thread(r);
            t.start();

            /**
             * PENDING How to handle exceptions from threads
             */
            try {
                t.join();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }

        } else {
            throw new HierarchyException("topClass has wrong type or View has wrong type");
        }
        return result;
    }

    /**
     * Returns the name of the class with the given ID
     * @return Name of the class
     * @param classID ID of class
     * Thrown if their are problems with accesing the hierarchy.
     */
    abstract boolean classExists(int classID);

    /**
     * Returns true if the class with the given name exists
     * @return True if the class exists
     * @param className Name of class
     * Thrown if their are problems with accesing the hierarchy.
     */
    abstract boolean classExists(String className);

    /**
     * Returns ID for class with name <code>className</code>.
     * @param className Name of class to find ID for.
     * Thrown if their are problems with accesing the hierarchy.
     * @return ID for class.
     */
    abstract int getID(String className);

    /**
     * Thread class performing classification.
     */
    class SimpleThread implements Runnable {

        /**
         * IDs for the data that this thread classifies.
         */
        Collection<Integer> dids;

        /**
         * View on the dataset.
         */
        SimpleView dv;

        /**
         * Class to classify with respect to.
         */
        SimpleHClass topClass;

        /**
         * Maximun depth of classifications.
         */
        int maxDepth;

        /**
         * Result of the classification.
         * This object is passed as a reference so all threads are writing to the same ClassifiedDataSetStorage.
         */
        ClassifiedDataSetStorage result;

        /**
         * Constructor.
         * 
         * @param dids IDs for the data that this thread classifies.
         * @param dv View on the dataset.
         * @param topClass View on the dataset.
         * @param maxDepth Maximun depth of classifications.
         * @param result Result of the classification.
         * This object is passed as a reference so all threads are writing to the same ClasClassifiedDataSetMem
         */
        public SimpleThread(Collection<Integer> dids, SimpleView dv, SimpleHClass topClass, int maxDepth, ClassifiedDataSetStorage result) {
            this.dids = dids;
            this.dv = dv;
            this.topClass = topClass;
            this.maxDepth = maxDepth;
            this.result = result;
        }

        /**
         * This method is invoked by getClassifications(long, long) and performs a recursive search for classifications for the class with ID <code>classID</code>
         * @return Returns the depth of the visited class.
         * Depth 0 means that the class is not part of the classification
         * @param res The result of the classification.
         * @param classID ID of class to find classification for
         * @param topClassID Classifications must be subclasses to this class
         * @param visited Set containing classes already visited in the search
         * @param classDepths Map from class ID to the class' depth.
         * A class' depth is found when it is visited in the search. Depth 0 means that the
         * class is not part of the classification
         * @param maxDepth The classes found must have a depth relative to the <code>topClassID</code>
         * of max <code>maxDepth</code> to be included in the classification
         * Thrown if their are problems with accesing the hierarchy.
         */
        // The parameters for this is described in the javaDoc
        // The method is called with a class to visit. The information that is has
        // been visited is saved in the set visited in order to avoid visiting the
        // same classes more than once.
        // First the method checks whther it is an immediate subclass of the top-Class
        // of the classification. If it is has depth 1. Even if it is it could also
        // be a transitive subclass. So in order to get all classifications it is
        // necessary to search through its superclassses (apart grom the top class itself)
        // in order to check whether or not they are part of the classification
        // When searchin through the superclasses we do not want to visit the same
        // classes more than once. Therefore the depth of every searched class must be
        // saved. This is done in classDepths. A depth of -1 means that the class is
        // not part if the classification. So when the method sees that a superclass
        // has been visited it retrieves the depth of this superclass from classDepths.
        // This is necessary in order to calculate the depth of the current class.
        // If the class is and immediate subclass to the top class the depth is 1,
        // otherwise it is the minimun of the depth of the subclasses belonging to
        // the classification + 1
        // After having visited all the superclasses we know whether or not the current
        // class is part of the classification. If it is, it is added to result
        // if not is depth is set to -1
        // the depth is saved in classDepths and returned
        private int recSearch(int classID, int topClassID, Set<Integer> res, int maxDepth,
                Set<Integer> visited, Map<Integer, Integer> classDepths) {

            // Add class to set of visited classes
            visited.add(classID);


            // The nice way
            int[] superclasses = getSuperclasses(classID);



            // Set dummy.depth
            int depth = Integer.MAX_VALUE;

            boolean found = false;

            // If the current class IS the top class depth = 0
            if (classID == topClassID) {
                found = true;
                depth = 0;
            } else {
                // Run through superclasses to see if actual class is immediate subclass
                // to topClass
                int i = 0;
                while ((i < superclasses.length) && !found) {
                    if (superclasses[i] == topClassID) {
                        found = true;
                        depth = 1;
                    }
                    i++;
                }

                // Assume that superclasses are not subclass to topClassID
                int tmpDepth = -1;
                for (int superclassID : superclasses) {

                    // If we have already visited a superclass do not visit it again
                    if (!visited.contains(superclassID)) {

                        tmpDepth = recSearch(superclassID, topClassID, res, maxDepth, visited, classDepths);
                    } // Instead retrieve it's previously calculated depth from classDepths
                    else {
                        tmpDepth = classDepths.get(superclassID);
                    }

                    // If the actual class is part of the classifacation find it's depth
                    if (tmpDepth != -1) {
                        found = true;
                        // depth is minimum of found depths
                        depth = Math.min(depth, tmpDepth + 1);
                    }
                }
            }
            // }
            if (found) {
                // Only save this class' ID if depth is below or equal to maxDepth
                if (depth <= maxDepth) {
                    res.add(classID);
                }
            // depth = -1 means that the class is not part of the classification
            } else {
                depth = -1;
            }

            // Save the depth of the class for later use.
            classDepths.put(classID, depth);

            return depth;
        }

        /**
         * Get classifications for a data point.
         * All classifications have a maximum depth of <code>maxDepth</code>.
         * @return Array containing classifications.
         * First dimension is depth. Second the classification at that depth.
         * @param maxDepth Maximum depth for classifications.
         * @param classID ID for the class corresponding to the relevant feauture from the datapoint.
         * @param topclassID Class to classify with respect to.
         * Thrown if their are problems with accesing the hierarchy.
         */
        // The following comment is meant to describe the layout of this method:
        // all the work is done in recSearch. getClassificationspoint does nothing
        // but convert the data it receceives from recSearch to the correct type
        // Furthermore it does some trivial checks on the validity of the input
        // Therefore the algorithm is described below (just above recSearch)
        private int[][] getClassificationsPoint(int classID, int topclassID, int maxDepth) {

            if (maxDepth <= 0) {
                return new int[0][0];
            }


            // Used to keep the classes that are part of the classification
            Set<Integer> res = new HashSet<Integer>();


            // Used to keep track of visited classes
            Set<Integer> visited = new HashSet<Integer>();

            // Used to keep the depth of all visited classes compared to topClassID.
            // If depth = -1 it means that the class is not a subclass to topClassID
            Map<Integer, Integer> classDepths = new HashMap<Integer, Integer>();



            // Start recursive search
            int depth = recSearch(classID, topclassID, res, maxDepth, visited, classDepths);


            // All the following is a very cumbersome way to convert the information contained in
            // visited and classDepths to an array which as its first dimension has the depth of the
            // classes contained in its second dimension. Off course only classes that are part of the
            // classification are contained in the array
            // Are lot of the work stems from the fact that visited and classDepths works with Long
            // whereas the array we return works with long (non-capital l)
            // Nonetheless this could proably be done more elegantly
            List<List<Integer>> returnList = new ArrayList<List<Integer>>();
            for (int i = 0; i <= maxDepth; i++) {
                returnList.add(i, new ArrayList<Integer>());
            }

            for (Integer cid : res) {
                int d = classDepths.get(cid);
                returnList.get(d).add(cid);

            }
            int[][] returnArray = new int[maxDepth + 1][0];

            for (int i = 0; i <= maxDepth; i++) {
                Integer[] tmpArrayL = returnList.get(i).toArray(new Integer[returnList.get(i).size()]);
                int[] tmpArrayl = new int[tmpArrayL.length];
                for (int j = 0; j < tmpArrayL.length; j++) {
                    tmpArrayl[j] = tmpArrayL[j];
                }
                returnArray[i] = tmpArrayl;
            }

            return returnArray;
        }

        /**
         * Initiates the classification.
         * The method that is run when the thread is started.
         */
        @Override
        public void run() {
            Map<HClass, List<Integer>> classification = new HashMap<HClass, List<Integer>>();
            Map<HClass, Integer> depts = new HashMap<HClass, Integer>();
            for (Iterator<Integer> it = dids.iterator(); it.hasNext();) {
                Integer did = it.next();

                try {

                    // Feature for dataPoint
                    String fea = dv.getFeature(did);


                    if (!classExists(topClass.getID())) {
                        System.err.println("Topclass id call to getClassifications does not exist.");
                        return;
                    }

                    int[][] classifications;

                    if (!classExists(fea)) {
                        System.err.println("UNCLASSIFIED: " + fea);
                        System.err.println(fea);

                    } else {

                        // Find ID for the given feature
                        int classID = getID(fea);

                        if (!superclassOf(topClass.getID(), classID) && !(classID==topClass.classID)) {
                            System.err.println("UNCLASSIFIED: " + topClass.getID() + " " + classID);
                            System.err.println("            : " + topClass.getName() + " " + fea);
                        //result.addUnclassified(did);
                        } else {

                            classifications = getClassificationsPoint(classID, topClass.getID(), maxDepth);

                            // Add classification to 'result' that is a classifiedDataSet
                            for (int d = 0; d < classifications.length; d++) {
                                for (int c : classifications[d]) {

                                    SimpleHClass clas = getHClass(c);

                                    List<Integer> classifiedByThisClass = classification.get(clas);
                                    if (classifiedByThisClass == null) {
                                        classifiedByThisClass = new ArrayList<Integer>();
                                        classification.put(clas, classifiedByThisClass);
                                    }
                                    classifiedByThisClass.add(did);
                                    depts.put(clas, d);
//                                    result.addClassification(clas, d);
//                                    result.addInstance(clas, did);
                                }
                            }
                        }
                    }
                } catch (HierarchyException ex) {
                    ex.printStackTrace();
                } catch (DataSetException ex) {
                    ex.printStackTrace();
                }

            }
            for (Entry<HClass, List<Integer>> e : classification.entrySet()) {
                result.addClassificationWithData(e.getKey(), depts.get(e.getKey()), e.getValue());
            }

        }
    }

    /**
     * Checks whether data has been added to the hierarchy
     * @return true if hierarchy is empty
     * Thrown if their are problems with accesing the hierarchy.
     */
    boolean isEmpty() {


        return Arrays.equals(new int[0], getAllClasses());
    }

    /**
     * Return a list of IDs for all classes in hierarchy
     * @return Array containings IDs for all classes in hierarchy
     * Thrown if their are problems with accesing the hierarchy.
     */
    public abstract int[] getAllClasses();

    /**
     * Loads hierarchy from file.
     * File has the following format;<br>
     * First line indicates hierarchy type, for instance:<br>
     * <code>Tree-hierarchy</code><br>
     * Following lines contains the hierarchy itself:<br>
     * <code> superclass: subclass1 ... subclassn </code>
     * @param filename file to save hierarchy in
     * Thrown if their are problems with accesing the hierarchy.
     * @throws java.io.IOException Thrown if the are read/write-problems.
     */
    private boolean load(String filename) throws IOException {

        //System.err.println("--- BEF ---\n"+toStringParam(true,true) + "\n----- BEF ------");
        boolean loaddisjunctive = false;

        if (isEmpty()) {



            BufferedReader input = new BufferedReader(new FileReader(filename));
            String line;
            line = input.readLine();
            if (!line.equals("SimpleHierarchy")) {
                throw new HierarchyException("Error. Wrong hierarchy type or invalid file format." + System.getProperty("newline") + "First line should be 'SimpleHierarchy'.");
            }
            line = input.readLine();
            if (line.equals("disjunctive")) {
                loaddisjunctive = true;
            } else {
                if (line.equals("non-disjuntive")) {
                    disjunctive = false;
                } else {
                    throw new HierarchyException("Error. Invalid file format." + System.getProperty("newline") + "Second line should be 'disjunctive' or 'non-disjunctive'.");
                }
            }

            while ((line = input.readLine()) != null) {
                if (line.contains(":")) {
                    // Split superclass from subclasses
                    String[] lineSplit = line.split(" : ");
                    String superclassName = lineSplit[0];
                    String subClassNamesStr = lineSplit[1];

                    // Split list of subclasses
                    String[] subclassNames = subClassNamesStr.split(" ");
                    for (String str : subclassNames) {
                        if (!(str.length() == 0)) {

                            // Check if class has already been added
                            // if not add class, and retrieve its ID
                            //System.err.println("ADD: " + superclassName + " : " + str);
                            int superclassID;
                            if (superclassName.equals("TOP")) {
                                superclassID = 0;
                            } else {
                                superclassID = addClass(superclassName);
                            }
                            //System.err.println("------\n"+toStringParam(true,true) + "\n-----------");

                            addSuperclass(superclassID, str);
                        }
                    }
                }
            }

        } else {
            throw new HierarchyException("Cannot load into a non-empty array");
        }
        return loaddisjunctive;

    }

    /**
     * Adds a new class to the hierarchy and returns its ID
     * A class without superclasses are placed as a subclass to TOP
     * @return ID for the added class
     * @param className name of the class to add
     * Thrown if their are problems with accesing the hierarchy.
     */
    int addClass(String className) {

        int[] superclasses = new int[0];
        return addClass(className, superclasses);
    }

    /**
     * Adds a new class to the hierarchy and returns its ID
     * @return ID for the added class
     * @param className name of the class to add
     * @param superclasses array of ID to the that the new class should have as superclasses
     * Thrown if their are problems with accesing the hierarchy.
     */
    abstract int addClass(String className, int[] superclasses);

    private int addSuperclass(int superclassID, String className) {

        int newClassID = addClass(className);
        //System.err.println("addSUperclass(long.String). superclassID: "+ superclassID + " className: " + className + " classID:" + newClassID);
        addSuperclass(superclassID, newClassID);
        return newClassID;
    }

    /**
     * Loads hierarchy from file.
     * File has the following format;<br>
     * First line indicates hierarchy type, for instance:<br>
     * <code>Tree-hierarchy</code><br>
     * Following lines contains the hierarchy itself:<br>
     * <code> subclass : superclass1 ... superclassn </code>
     * @param filename file to save hierarchy in
     * Exception thrown when there are problems accesing the hierarchy.
     * @throws java.io.IOException Thrown if there are read/write-problems.
     */
    boolean load2(String filename) throws IOException {

        boolean loaddisjunctive = false;

        if (isEmpty()) {



            BufferedReader input = new BufferedReader(new FileReader(filename));
            String line;

            line = input.readLine();
            if (!line.equals("SimpleHierarchy")) {
                System.err.println(line);
                throw new HierarchyException("Error. Wrong hierarchy type or invalid file format." + newline + "First line should be 'SimpleHierarchy'.");
            }
            line = input.readLine();
            if (line.equals("disjunctive")) {
                loaddisjunctive = true;
            } else {
                if (line.equals("non-disjuntive")) {
                    disjunctive = false;
                } else {
                    throw new HierarchyException("Error. Invalid file format." + newline + "Second line should be 'disjunctive' or 'non-disjunctive'.");
                }
            }


            while ((line = input.readLine()) != null) {
                if (line.contains(":")) {
                    // Split superclass from subclasses
                    String[] lineSplit = line.split(" : ");
                    String subclassName = lineSplit[0];
                    String superClassNamesStr = lineSplit[1];

                    int subclassID = addClass(subclassName);
                    // Split list of subclasses
                    String[] superclassNames = superClassNamesStr.split(" ");
                    for (String str : superclassNames) {
                        if (!(str.length() == 0)) {

                            int superclassID = addClass(str);
                            addSuperclass(superclassID, subclassID);
                        }
                    }
                }
            }

        } else {
            throw new HierarchyException("Cannot load into a non-empty array");
        }

        return loaddisjunctive;
    }

    abstract void addSuperclass(int superclassID, int classID);

    @Override
    public boolean superclassOf(HClass c1, HClass c2) {

        HClassTuple key = new HClassTuple(c1, c2);
        Boolean s = superClassOfCache.get(key);
        if (s != null) {
            return s;
        }

        if (!((c1 instanceof SimpleHClass) && (c2 instanceof SimpleHClass))) {
            return false;
        }

        SimpleHClass sc1 = (SimpleHClass) c1;
        SimpleHClass sc2 = (SimpleHClass) c2;

        s = superclassOf(sc1.getID(), sc2.getID());
        superClassOfCache.put(key, s);
        return s;
    }

    /**
     * Returns true if class with id <code>c1id</code> is superclass to class with id  <code>c2id</code>.
     * @param c1id Id of possible superclass.
     * @param c2id ID of possible subclass.
     * Thrown if there are problems accesing the hierarchy.
     * @return True if  <code>c1id</code if superclass of  <code>c2id</code.
     */
    private boolean superclassOf(int c1id, int c2id) {

        if (c2id == 0) {
            if (c1id == 0) {
                return true;
            } else {
                return false;
            }
        }

        boolean found = false;
        int[] superclasses = getSuperclasses(c2id);

        // Depth-first search through superclasses
        for (int i = 0; (i < superclasses.length) && (!found); i++) {
            int sc = superclasses[i];
            if (sc == c1id) {
                found = true;
            } else {
                found = superclassOf(c1id, sc);
            }
        }

        return found;
    }

    /**
     * Method for getting disjuntion between two classes in a disjunctive hierarchy.
     * @param c1 First class to use in disjunction.
     * @param c2 First class to use in disjunction.
     * @return Class representing disjunction between to classes.
     * Null if
     * Thrown if their are problems with accesing the hierarchy.
     */
    private SimpleHClass getIntersectDisj(SimpleHClass c1, SimpleHClass c2) {

        if (superclassOf(c1.getID(), c2.getID())) {
            return c2;
        } else {
            if (superclassOf(c2.getID(), c1.getID())) {
                return c1;
            } else {
                return null;
            }
        }

    }

    @Override
    public Set<HClass> getIntersection(HClass c1, HClass c2) {
//System.err.println("AbsSimple getIntersection " + c1.getName() + "," + c2.getName());
        if (disjunctive) {
//System.err.println("DISJ");
            Set<HClass> res = new HashSet<HClass>();

            SimpleHClass r = getIntersectDisj((SimpleHClass) c1, (SimpleHClass) c2);
            if (r != null) {
                res.add(r);
            }
            //    System.err.println("RES: " + res);
            return res;
        } else {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

    /**
     * Method for getting disjuntion between classes in a non-disjunctive hierarchy.
     * @param classes Classes to find disjuntion for.
     * Thrown if their are problems with accesing the hierarchy.
     * @return Classes representing disjunction of input.
     */
    @Override
    public Set<HClass> getIntersection(HClass[] classes) {

        Set<HClass> result = new HashSet<HClass>();


        for (HClass c : classes) {
            if (!(c instanceof SimpleHClass)) {
                System.err.println("At least one class is not a SimpleHClass");
                return result;
            }
        }

        // System.out.println("disj: " + disjunctive);

        if (disjunctive) {

            SimpleHClass res;

            res = (SimpleHClass) classes[0];
            for (int i = 1; (i < classes.length) && (res != null); i++) {

                res = getIntersectDisj(res, (SimpleHClass) classes[i]);
            }
            if (res != null) {
                result.add(res);
            }
        } else {
            Set<HClass> removeSet = new HashSet<HClass>();
            for (int i = 0; i < classes.length; i++) {
                SimpleHClass ci = (SimpleHClass) classes[i];
                for (int j = i + 1; j < classes.length; j++) {
                    SimpleHClass cj = (SimpleHClass) classes[j];
                    if (superclassOf(ci.getID(), cj.getID())) {
                        removeSet.add(ci);
                    } else {
                        if (superclassOf(cj.getID(), ci.getID())) {
                            removeSet.add(cj);
                        }
                    }
                }
            }
            result = new HashSet<HClass>(Arrays.asList(classes));
            result.removeAll(removeSet);
        }

        //     System.err.println("Simple.GetInterSect:   IN: " + Arrays.toString(classes) + "    OUT: " + result);
        return result;

    }

    @Override
    public String toString() {
        try {
            return toStringParam(false, true);
        } catch (HierarchyException ex) {
            ex.printStackTrace();
        }
        return "";
    }

    @Override
    public String toStringParam(boolean printID, boolean printName) {

        String str = "";

        for (int i = 0; i < classCounter; i++) {

            int[] superclasses = getSuperclasses(i);
            for (int j = 0; j < superclasses.length; j++) {
                String superclassStr = "";
                String subclassStr = "";
                if (printID) {
                    superclassStr = "" + superclasses[(int) j];
                    subclassStr = "" + i;
                }
                if (printID && printName) {
                    superclassStr = superclassStr + "-";
                    subclassStr = subclassStr + "-";
                }
                if (printName) {
                    superclassStr = superclassStr + getName(superclasses[(int) j]);
                    subclassStr = subclassStr + getName(i);
                }
                str = str + superclassStr + ": " + subclassStr + newline;
            }

        }
        return str;
    }

    @Override
    public boolean isClassifiedBy(Integer did, View dv, HClass classification) {
        if (dv instanceof SimpleView) {
            if (classification instanceof SimpleHClass) {
                try {
                    SimpleView sdv = (SimpleView) dv;
                    SimpleHClass sclas = (SimpleHClass) classification;

                    String fea = sdv.getFeature(did);
                    int classID = getID(fea);
                    int superclassID = sclas.getID();
                    //     System.err.println(getName(classID) + " classBy " + getName(superclassID) + " :: " + superclassOf(superclassID,classID));
                    if (classID == superclassID) {
                        return true;
                    }
                    return superclassOf(superclassID, classID);
                } catch (DataSetException ex) {
                    throw new HierarchyException("Could not acces dataset: " + ex.getMessage());
                }

            } else {
                throw new HierarchyException("classification is not in this class");
            }
        } else {
            throw new HierarchyException("Wrong view type. Should be a SimpleView");
        }
    }

    @Override
    public String getType() {
        return "SimpleHierarchy";
    }
}

